import json
import os
from datetime import datetime
from functools import wraps
from flask import Flask, render_template, request, redirect, url_for, session, flash, jsonify, send_file
from werkzeug.security import generate_password_hash, check_password_hash
from flask_sqlalchemy import SQLAlchemy
from models import db, User, Vote
import io
import csv
# 使用CSV格式导出数据

app = Flask(__name__)
app.secret_key = 'change_this_secret_key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///vote.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db.init_app(app)

CONFIG_PATH = 'config.json'

# 读取配置
def load_config():
    with open(CONFIG_PATH, 'r', encoding='utf-8') as f:
        return json.load(f)

def sync_users_from_config():
    config = load_config()
    for u in config.get('users', []):
        if not User.query.filter_by(username=u['username']).first():
            user = User(username=u['username'], password=generate_password_hash(u['password']), role=u.get('role', 'voter'))
            db.session.add(user)
    db.session.commit()


# 登录限制装饰器
def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        config = load_config()
        if not config.get('allow_anonymous', False) and 'user' not in session:
            flash('请先登录')
            return redirect(url_for('login', next=request.url))
        return f(*args, **kwargs)
    return decorated_function

# 导出投票数据（仅管理员可访问）
@app.route('/export_votes')
def export_votes():
    if 'user' not in session or session['user'].get('role') != 'admin':
        flash('无权限')
        return redirect(url_for('index'))
    votes = Vote.query.all()
    rows = []
    for v in votes:
        user = User.query.get(v.user_id) if v.user_id else None
        rows.append({
            '投票ID': v.id,
            '用户名': user.username if user else '匿名',
            '主题ID': v.theme_id,
            '轮次ID': v.round_id,
            '选项ID': v.option_ids,
            '投票时间': v.voted_at.strftime('%Y-%m-%d %H:%M:%S'),
            'IP': v.ip or ''
        })
    output = io.StringIO()
    writer = csv.DictWriter(output, fieldnames=rows[0].keys() if rows else ['投票ID','用户名','主题ID','轮次ID','选项ID','投票时间','IP'])
    writer.writeheader()
    writer.writerows(rows)
    output.seek(0)
    return send_file(io.BytesIO(output.read().encode('utf-8-sig')), download_name='votes.csv', as_attachment=True)

# 首页：主题列表
@app.route('/')
def index():
    config = load_config()
    return render_template('index.html', config=config)

# 登录
@app.route('/login', methods=['GET', 'POST'])
def login():
    config = load_config()
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        user = User.query.filter_by(username=username).first()
        if user and check_password_hash(user.password, password):
            session['user'] = {'id': user.id, 'username': user.username, 'role': user.role, 'is_anonymous': user.is_anonymous}
            flash('登录成功')
            return redirect(url_for('index'))
        flash('用户名或密码错误')
    return render_template('login.html', config=config)

# 注册
@app.route('/register', methods=['GET', 'POST'])
def register():
    config = load_config()
    if not config.get('allow_anonymous', False):
        if request.method == 'POST':
            username = request.form['username']
            password = request.form['password']
            if User.query.filter_by(username=username).first():
                flash('用户名已存在')
            else:
                user = User(username=username, password=generate_password_hash(password), role='voter')
                db.session.add(user)
                db.session.commit()
                flash('注册成功，请登录')
                return redirect(url_for('login'))
        return render_template('register.html', config=config)
    else:
        return redirect(url_for('index'))

# 匿名登录
@app.route('/anonymous_login')
def anonymous_login():
    config = load_config()
    if config.get('allow_anonymous', False):
        # 用IP或唯一标识生成匿名用户
        anon_id = f"anon-{request.remote_addr.replace('.', '-')}-{int(datetime.now().timestamp())}"
        user = User(username=anon_id, password='', is_anonymous=True)
        db.session.add(user)
        db.session.commit()
        session['user'] = {'id': user.id, 'username': user.username, 'role': user.role, 'is_anonymous': True}
        flash('匿名登录成功')
        return redirect(url_for('index'))
    else:
        return redirect(url_for('login'))

# 登出
@app.route('/logout')
def logout():
    session.pop('user', None)
    flash('已退出登录')
    return redirect(url_for('index'))

# 主题详情页，显示所有轮次
@app.route('/theme/<theme_id>')
def theme_detail(theme_id):
    from datetime import datetime
    config = load_config()
    theme = next((t for t in config['themes'] if t['id'] == theme_id), None)
    if not theme:
        flash('主题不存在')
        return redirect(url_for('index'))
    now = datetime.now()
    for round_ in theme['rounds']:
        try:
            start = datetime.strptime(round_['start_time'], "%Y-%m-%d %H:%M:%S")
            end = datetime.strptime(round_['end_time'], "%Y-%m-%d %H:%M:%S")
            if start <= now <= end:
                round_['time_status'] = 'active'
            elif now < start:
                round_['time_status'] = 'future'
            else:
                round_['time_status'] = 'ended'
        except Exception:
            round_['time_status'] = 'invalid'
    return render_template('theme.html', config=config, theme=theme)

# 投票页
@app.route('/theme/<theme_id>/round/<round_id>', methods=['GET', 'POST'])
def vote(theme_id, round_id):
    config = load_config()
    theme = next((t for t in config['themes'] if t['id'] == theme_id), None)
    if not theme:
        flash('主题不存在')
        return redirect(url_for('index'))
    round_ = next((r for r in theme['rounds'] if r['id'] == round_id), None)
    if not round_:
        flash('轮次不存在')
        return redirect(url_for('theme_detail', theme_id=theme_id))
    from datetime import timezone
    now = datetime.now()
    # 兼容无时区的"YYYY-MM-DD HH:MM:SS"格式
    try:
        start = datetime.strptime(round_['start_time'], "%Y-%m-%d %H:%M:%S")
        end = datetime.strptime(round_['end_time'], "%Y-%m-%d %H:%M:%S")
    except Exception as e:
        flash('投票起止时间配置有误')
        return redirect(url_for('theme_detail', theme_id=thejme_id))
    if not (start <= now <= end):
        start_str = start.strftime('%Y-%m-%d %H:%M')
        end_str = end.strftime('%Y-%m-%d %H:%M')
        flash(f'当前不在投票时间范围内（{start_str} ~ {end_str}）')
        return redirect(url_for('theme_detail', theme_id=theme_id))

    if not config.get('allow_anonymous', False) and 'user' not in session:
        flash('请先登录')
        return redirect(url_for('login', next=request.url))

    user = None
    voted = []
    if 'user' in session:
        user = User.query.get(session['user']['id'])
        # 查找是否已经投票
        vote_rec = Vote.query.filter_by(user_id=user.id, theme_id=theme_id, round_id=round_id).first()
        if vote_rec:
            voted = vote_rec.option_ids.split(',')
    elif config.get('allow_anonymous', False):
        user_ip = request.remote_addr
        vote_rec = Vote.query.filter_by(ip=user_ip, theme_id=theme_id, round_id=round_id).first()
        if vote_rec:
            voted = vote_rec.option_ids.split(',')
    else:
        flash('请先登录')
        return redirect(url_for('login', next=request.url))

    if request.method == 'POST' and not voted:
        selected = request.form.getlist('option')
        if len(selected) == 0 or len(selected) > round_['max_votes_per_user']:
            flash(f'请选择{round_["max_votes_per_user"]}个以内的选项')
        else:
            if user:
                vote_rec = Vote(user_id=user.id, theme_id=theme_id, round_id=round_id, option_ids=','.join(selected), ip=request.remote_addr)
            else:
                vote_rec = Vote(user_id=None, theme_id=theme_id, round_id=round_id, option_ids=','.join(selected), ip=request.remote_addr)
            db.session.add(vote_rec)
            db.session.commit()
            flash('投票成功！')
            return redirect(url_for('vote_result', theme_id=theme_id, round_id=round_id))

    return render_template('vote.html', config=config, theme=theme, round=round_, voted=voted)

# 投票结果页
@app.route('/theme/<theme_id>/round/<round_id>/result')
def vote_result(theme_id, round_id):
    config = load_config()
    theme = next((t for t in config['themes'] if t['id'] == theme_id), None)
    if not theme:
        flash('主题不存在')
        return redirect(url_for('index'))
    round_ = next((r for r in theme['rounds'] if r['id'] == round_id), None)
    if not round_:
        flash('轮次不存在')
        return redirect(url_for('theme_detail', theme_id=theme_id))
    if not round_.get('public_result', False):
        flash('本轮结果暂不公开')
        return redirect(url_for('theme_detail', theme_id=theme_id))
    # 统计结果
    counts = {opt['id']: 0 for opt in round_['options']}
    votes = Vote.query.filter_by(theme_id=theme_id, round_id=round_id).all()
    for v in votes:
        for opt in v.option_ids.split(','):
            if opt in counts:
                counts[opt] += 1
    results = [
        {"label": next(opt['label'] for opt in round_['options'] if opt['id'] == oid), "count": count}
        for oid, count in counts.items()
    ]
    return render_template('result.html', config=config, theme=theme, round=round_, results=results)

# 导出当前轮次投票结果
@app.route('/theme/<theme_id>/round/<round_id>/export')
def export_round_result(theme_id, round_id):
    config = load_config()
    theme = next((t for t in config['themes'] if t['id'] == theme_id), None)
    if not theme:
        flash('主题不存在')
        return redirect(url_for('index'))
    round_ = next((r for r in theme['rounds'] if r['id'] == round_id), None)
    if not round_:
        flash('轮次不存在')
        return redirect(url_for('theme_detail', theme_id=theme_id))
    if not round_.get('public_result', False):
        flash('本轮结果暂不公开')
        return redirect(url_for('theme_detail', theme_id=theme_id))
    
    # 统计结果
    counts = {opt['id']: 0 for opt in round_['options']}
    votes = Vote.query.filter_by(theme_id=theme_id, round_id=round_id).all()
    for v in votes:
        for opt in v.option_ids.split(','):
            if opt in counts:
                counts[opt] += 1
    
    # 准备导出数据
    export_data = [
        {
            "选项": next(opt['label'] for opt in round_['options'] if opt['id'] == oid),
            "票数": count,
            "比例": f"{(100*count/(sum(counts.values()) or 1)):.1f}%"
        }
        for oid, count in counts.items()
    ]
    
    filename = f"{theme['title']}_{round_['title']}投票结果"
    
    output = io.StringIO()
    if export_data:
        writer = csv.DictWriter(output, fieldnames=export_data[0].keys())
        writer.writeheader()
        writer.writerows(export_data)
    output.seek(0)
    return send_file(io.BytesIO(output.read().encode('utf-8-sig')), download_name=f"{filename}.csv", as_attachment=True)

if __name__ == '__main__':
    with app.app_context():
        db.create_all()
        sync_users_from_config()
    app.run(debug=True)